﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the GNU General Public License version 2 (GPLv2).
// See accompanying file License.txt or copy at http://phuse.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Phuse.Net
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;
    using System.Text;

    /// <summary>Represents the base of all single-line commands where an object of the type representing the response
    /// can be created by passing four arguments of custom type and a <see cref="Stream"/> object.</summary>
    /// <typeparam name="TClient">The type of the client the command is for.</typeparam>
    /// <typeparam name="TResponse">The type representing the response.</typeparam>
    /// <typeparam name="TP1">The type of the first custom parameter needed to create a
    /// <typeparamref name="TResponse"/> object, see remarks.</typeparam>
    /// <typeparam name="TP2">The type of the second custom parameter needed to create a
    /// <typeparamref name="TResponse"/> object, see remarks.</typeparam>
    /// <typeparam name="TP3">The type of the third custom parameter needed to create a
    /// <typeparamref name="TResponse"/> object, see remarks.</typeparam>
    /// <typeparam name="TP4">The type of the fourth custom parameter needed to create a
    /// <typeparamref name="TResponse"/> object, see remarks.</typeparam>
    /// <remarks>
    /// <para><typeparamref name="TP1"/> - <typeparamref name="TP4"/> correspond to the types of the 2nd - 5th words of
    /// the initial response line.</para>
    /// <para>The types represented by <typeparamref name="TP1"/> - <typeparamref name="TP4"/> must either be strings or
    /// expose a method with the following signature:
    /// <code>public static TP Parse(string s, IFormatProvider provider)</code>
    /// where <c>TP</c> is equal to the type exposing the Parse method.</para>
    /// <para>Concrete commands never derive directly from this class, but subclass protocol-specific variants, like
    /// e.g. <see cref="Nntp.SingleLineCommand{T, U, V, W, X}"/>.</para>
    /// </remarks>
    /// <threadsafety static="true" instance="false"/>
    [SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes", Justification = "We absolutely need more than two type parameters.")]
    public abstract class SingleLineCommandBase<TClient, TResponse, TP1, TP2, TP3, TP4> :
        SingleLineCommandBase<TClient, TResponse>
        where TClient : IClient<TClient>
        where TResponse : IResponse
    {
        internal SingleLineCommandBase(
            byte[] command, Encoding initialResponseLineEncoding, params string[] successResponseCodes) :
            base(command, initialResponseLineEncoding, 6, successResponseCodes)
        {
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>When overridden in a derived class constructs a <typeparamref name="TResponse"/> object
        /// from the arguments passed for <paramref name="p1"/> - <paramref name="p4"/> as well as
        /// <paramref name="subsequentLines"/> and returns the <typeparamref name="TResponse"/> object.</summary>
        /// <param name="p1">An object representing the second word of the initital response line.</param>
        /// <param name="p2">An object representing the third word of the initital response line.</param>
        /// <param name="p3">An object representing the fourth word of the initital response line.</param>
        /// <param name="p4">An object representing the fifth word of the initital response line.</param>
        /// <param name="subsequentLines">A stream containing the subsequent lines of the response, if any.</param>
        /// <remarks>The first word of the initial response line is the response code (which has already been checked).
        /// </remarks>
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "These are generic parameter names.")]
        protected abstract TResponse CreateResponse(TP1 p1, TP2 p2, TP3 p3, TP4 p4, Stream subsequentLines);

        /// <summary>See <see cref="SingleLineCommandBase{T, U}.CreateResponse"/>.</summary>
        /// <exception cref="InvalidOperationException"><typeparamref name="TP1"/> and/or <typeparamref name="TP2"/>
        /// and/or <typeparamref name="TP3"/> and/or <typeparamref name="TP4"/> do not offer a Parse method with
        /// required signature, see class remarks.</exception>
        /// <remarks>Parses the words at the indices 1 - 4 of
        /// <paramref name="responseStream"/>.<see cref="ResponseStream.InitialLineWords"/> with the Parse
        /// methods of respective types represented by <typeparamref name="TP1"/> - <typeparamref name="TP4"/>. Then
        /// calls <see cref="SingleLineCommandBase{T, U, V, W, X, Y}.CreateResponse(V, W, X, Y, Stream)"/>
        /// with the results as arguments and returns the new result.</remarks>
        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", Justification = "Method is not supposed to be called by external code.")]
        protected sealed override TResponse CreateResponse(ResponseStream responseStream)
        {
            return this.CreateResponse(
                Parser<TP1>.Parse(responseStream.InitialLineWords[1]),
                Parser<TP2>.Parse(responseStream.InitialLineWords[2]),
                Parser<TP3>.Parse(responseStream.InitialLineWords[3]),
                Parser<TP4>.Parse(responseStream.InitialLineWords[4]),
                responseStream);
        }
    }
}
